/*
 ============================================================================
 Name        : FirstPass.c
 Author      : Sherry Krikszer
 Version     : 1.0
 Copyright   :
 Description : in C, Ansi-style
 ============================================================================
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "FirstPass.h"
#include "AssemblerCommon.h"
#include "Common.h"
#include "CommandParser.h"

/* ----------------------------------------------------------------------------
 *	Constants Definition
 * ----------------------------------------------------------------------------*/

#define STRING_PARAM_LOCATION 0

#define STRING_TERMINATOR 0

/* ----------------------------------------------------------------------------
 *	Constants Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Macro Definition
 * ----------------------------------------------------------------------------*/

#define IS_EMPTY_LINE(string) ((strcmp(string, EMPTY_LINE) == EQUAL_STRINGS) ? TRUE : FALSE)

/* ----------------------------------------------------------------------------
 *	Macro Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Declaration
 * ----------------------------------------------------------------------------*/

Boolean DoesSymbolExist			(char* symbolNameToCheck, SymbolTable* symbolTable);

/* ----------------------------------------------------------------------------
 *	Function Declaration End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Definition
 * ----------------------------------------------------------------------------*/

Boolean PerformFirstPass(ProgramLines* programLines, Memory* programMemory,
		   SymbolTable* symbolTable, SymbolTable* externSymbolTable)
{
	Boolean firstPassSucceeded = TRUE;
	int index;

	/* 1. Initialize program counters */
	programMemory->DC = 0;
	programMemory->IC = 0;

	/* 1. Initialize symbol table counters */
	symbolTable->SC = 0;
	externSymbolTable->SC = 0;

	for (index = 0; index < programLines->lineCounter; index++)
	{
		Boolean DoesContainSymbol = FALSE;

		/* 2. Get the current line */
		ParsedProgramLine* currProgramLine = &(programLines->lines[index]);

		/* Get the command tokens from the current line */
		CommandLineTokens* currCommandTokens = currProgramLine->commandTokens;

		currProgramLine->commadParsedOperands = NULL;

		// check if actually need to handle line
		if (currProgramLine->shouldBeHandled == FALSE)
		{
			continue;
		}

		// Check that the command tokens where created successfully, if not a parsing error occured
		if ((currProgramLine->parseErrorOccured == TRUE) || currCommandTokens == NULL)
		{
			continue;
		}

		/* 3. Check if the current line contains a symbol */
		if ((IS_EMPTY_LINE(currCommandTokens->symbol) == FALSE) && currCommandTokens->symbol != LINE_END)
		{
			/* 4. Set the 'DoesContainSymbol' symbol flag to true */
			DoesContainSymbol = TRUE;
		}

		/* Check for a directive */
		if (currCommandTokens->statementType == DIRECTIVE)
		{
			/* 5. Check for a pseudo instruction directive (.data / .string) */
			Boolean DoesContainDataDirective = FALSE;
			Boolean DoesContainStringDirective = FALSE;

			if (strcmp(currCommandTokens->statement.directive, DATA_DIRECTIVE) == EQUAL_STRINGS)
			{
				DoesContainDataDirective = TRUE;
			}
			if (strcmp(currCommandTokens->statement.directive, STRING_DIRECTIVE) == EQUAL_STRINGS)
			{
				DoesContainStringDirective = TRUE;
			}

			/* In case it is a pseudo instruction (.data / .string) */
			if ((DoesContainDataDirective == TRUE) || (DoesContainStringDirective == TRUE))
			{
				/* 6. If a symbol was found insert it to the symbol table as a data symbol */
				if (DoesContainSymbol == TRUE)
				{
					Symbol currSymbol;
					currSymbol.offset = programMemory->DC;
					strncpy(currSymbol.symbolName, currCommandTokens->symbol, MAX_SYMBOL_NAME_LENGTH);
					currSymbol.symbolType = DATA;

					symbolTable->symbols[symbolTable->SC] = currSymbol;
					symbolTable->SC++;
				}

				/* 7. Classify the data type (.data / .string),
				 * store it in the memory and update data counters */
				if (DoesContainDataDirective == TRUE)
				{
					int index = 0;
					Boolean hasErrorBeenFound = FALSE;

					while ((hasErrorBeenFound == FALSE) &&
						   (index < currCommandTokens->params.paramCounter))
					{
						char* currNumericParameter =
								currCommandTokens->params.paramValues[index];

						ErrorMessage currentError;

						if (IsValidNumberParameter(currNumericParameter) == TRUE)
						{
							int numericOperandValue = atoi(currNumericParameter);

							MemoryWord currDataWord;

							/* Address would be adjusted at the end of the first pass
							 * (Data segment follows code segment and should be adjusted accordingly) */
							currDataWord.offset = 0;
							currDataWord.wordType = DATA_WORD;
							currDataWord.wordEncoding.DataWord = numericOperandValue;

							programMemory->dataSegment[programMemory->DC] = currDataWord;
							programMemory->DC++;
						}
						else
						{
							/* Notify data directive parameters are invalid */
							sprintf(currProgramLine->errorMessage,
									"Invalid .data directive, "
									"non numeric value found for parameter at position %d : %s",
									index, currentError);

							hasErrorBeenFound = TRUE;
						}

						index++;
					}
				}
				else if (DoesContainStringDirective == TRUE)
				{
					char* currStringParameter =
							currCommandTokens->params.paramValues[STRING_PARAM_LOCATION];

					if (IsValidStringParameter(currStringParameter, currProgramLine->errorMessage) == TRUE)
					{
						MemoryWord currDataWord;

						/* Move passed the string start marker */
						currStringParameter++;

						/* Handle string directive by going over all characters until string end marker */
						while (*currStringParameter != STRING_END_MARKER)
						{
							/* Address would be adjusted at the end of the first pass
							 * (Data segment follows code segment and should be adjusted accordingly) */
							currDataWord.offset = 0;
							currDataWord.wordType = DATA_WORD;
							currDataWord.wordEncoding.DataWord = *currStringParameter;

							programMemory->dataSegment[programMemory->DC] = currDataWord;
							programMemory->DC++;

							currStringParameter++;
						}

						/* Add string terminator word */
							
						currDataWord.offset = 0;
						currDataWord.wordType = DATA_WORD;
						currDataWord.wordEncoding.DataWord = STRING_TERMINATOR;

						programMemory->dataSegment[programMemory->DC] = currDataWord;
						programMemory->DC++;

					}
				}
			}
			else
			{
				/* 8. Check for other instruction directives (.extern / .entry) */
				Boolean DoesContainExternDirective = FALSE;
				Boolean DoesContainEntryDirective = FALSE;

				if (strcmp(currCommandTokens->statement.directive, EXTERN_DIRECTIVE) == EQUAL_STRINGS)
				{
					DoesContainExternDirective = TRUE;
				}

				if (strcmp(currCommandTokens->statement.directive, ENTRY_DIRECTIVE) == EQUAL_STRINGS)
				{
					DoesContainEntryDirective = TRUE;
				}

				if ((DoesContainExternDirective == TRUE) || (DoesContainEntryDirective == TRUE))
				{
					/* 9. In case it is an .extern instruction directive */
					if (DoesContainExternDirective == TRUE)
					{
						if (currCommandTokens->params.paramCounter == EXPECTED_EXTERN_PARAM_COUNT)
						{
							int externSymbolLength = strlen(currCommandTokens->
									params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION]);

							if (externSymbolLength > 0)
							{
								if (IsValidLabel(currCommandTokens->
										params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION],
										currProgramLine->errorMessage) == TRUE)
								{
									/* Check that the symbol doesn't already exists */
									if ((DoesSymbolExist(currCommandTokens->
											params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION],
											externSymbolTable) == TRUE) ||
										(DoesSymbolExist(currCommandTokens->
											params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION],
											symbolTable) == TRUE))
									{
										/* Notify extern symbol name already exists */
										sprintf(currProgramLine->errorMessage,
												"Invalid extern symbol name, "
												"\"%s\" has already been defined as a symbol",
												currCommandTokens->
													params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION]);
									}
									else
									{
										/* Insert the given symbol to the extern symbol list */
										Symbol currExternSymbol;
										currExternSymbol.offset = 0;

										strcpy(currExternSymbol.symbolName, currCommandTokens->
												params.paramValues[EXTERN_PARAM_SYMBOL_LOCATION]);
										currExternSymbol.symbolType = EXTERN;

										externSymbolTable->symbols[externSymbolTable->SC] =
												currExternSymbol;
										externSymbolTable->SC++;
									}
								}
							}
							else
							{
								/* Notify invalid extern symbol name */
								sprintf(currProgramLine->errorMessage,
										"Invalid extern symbol name, extern symbol name cannot be empty");
							}
						}
						else
						{
							if (currCommandTokens->params.paramCounter < EXPECTED_EXTERN_PARAM_COUNT)
							{
								/* Notify invalid extern symbol name */
								sprintf(currProgramLine->errorMessage,
										"Invalid extern symbol name, extern symbol name cannot be empty");
							}
							else if(currCommandTokens->params.paramCounter >
								EXPECTED_EXTERN_PARAM_COUNT)
							{
								/* Notify invalid extern symbol name */
								sprintf(currProgramLine->errorMessage,
										"Invalid extern symbol name, only one name expected");
							}
						}
					}
				}
			}
		}
		/* In case it doesn't contain other instruction directives it may be a code instruction*/
		else
		{
			/* 11. If a symbol was found insert it to the symbol table as a code symbol */
			if (DoesContainSymbol == TRUE)
			{
				Symbol currSymbol;
				currSymbol.offset = programMemory->IC;
				strcpy(currSymbol.symbolName, currCommandTokens->symbol);
				currSymbol.symbolType = CODE;

				symbolTable->symbols[symbolTable->SC] = currSymbol;
				symbolTable->SC++;
			}

			/* TODO : check that the rest of the line is a valid command,
			 * if it isn't notify an error */

			/* In case this is a command line */
			if (currCommandTokens->statementType == COMMAND)
			{
				ParseResult currCommandResult =
						ParseAssemblerCommand(currCommandTokens->statement.commandName,
									  currCommandTokens->params);

				if (currCommandResult.hasParseSucceded == TRUE)
				{
					/* Reserve room for command operands */
					programMemory->IC += currCommandResult.parsedCommand.sizeInWords;

					currProgramLine->commadParsedOperands = InitCommandParsedOperands();

					*(currProgramLine->commadParsedOperands) = currCommandResult.parsedCommand;

				}
				else
				{
					firstPassSucceeded = FALSE;
				}
			}
			/* TODO : evaluate the rest of the command and calculate it's length in words */
			/*programMemory.IC =*/
		}
	}

	return firstPassSucceeded;
}

CommandOperands* InitCommandParsedOperands()
{
	CommandOperands* commandOperands = (CommandOperands*) malloc (sizeof (CommandOperands));

	return commandOperands;
}

void FreeCommandParsedOperands(CommandOperands* commandOperands)
{
	if (commandOperands != NULL)
	{
		free(commandOperands);
		commandOperands = NULL;
	}
}

Boolean DoesSymbolExist(char* symbolNameToCheck, SymbolTable* symbolTable)
{
	Boolean symbolAlreadyExist = FALSE;

	int index;

	for (index = 0; index < symbolTable->SC; index++)
	{
		char* currentSymbolName = symbolTable->symbols[index].symbolName;

		if (strcmp(currentSymbolName, symbolNameToCheck) == EQUAL_STRINGS)
		{
			symbolAlreadyExist = TRUE;
		}
	}

	return symbolAlreadyExist;
}

//Boolean PerformFirstPass(List* dividedProgramLines, Memory programMemory, Register programRegisters[],
//		   SymbolTable symbolTable, SymbolTable externSymbolTable)
//{
//	Boolean firstPassSucceeded = TRUE;
//
//	/* Initialize program counters */
//	programMemory.DC = 0;
//	programMemory.IC = 0;
//
//	symbolTable.SC = 0;
//	externSymbolTable.SC = 0;
//
//	/* Set the current line to the first */
//	Node_ptr currProgramLine = dividedProgramLines->head;
//
//	while (currProgramLine != List_End())
//	{
//		/* Load the command tokens from the current line */
//		CommandLineTokens* currCommandTokens = (CommandLineTokens*) currProgramLine->data;
//
//		if (currCommandTokens->symbol != LINE_END)
//		{
//			/* TODO : check that the given symbol isn't a cmd name or a register name */
//			if (DoesContainCommand(currCommandTokens->symbol) == TRUE)
//			{
//				firstPassSucceeded = FALSE;
//			}
//			else
//			{
//
//			}
//		}
//
//		/* Check for a pseudo instruction directive (.data / .string) */
//		if (strcmp(currProgramLine + currSymbolIndex, DATA_DIRECTIVE  ) == EQUAL_STRINGS ||
//			strcmp(currProgramLine + currSymbolIndex, STRING_DIRECTIVE) == EQUAL_STRINGS)
//		{
//			/* In case it is a pseudo instruction
//			 * If a symbol was found insert it to the symbol table as a data symbol */
//			if (DoesContainSymbol == TRUE)
//			{
//				Symbol currSymbol;
//				currSymbol.address = programMemory.DC;
//				strncpy(currSymbol.symbolName, currCommandTokens, currSymbolIndex -1);
//				currSymbol.symbolType = DATA;
//
//				symbolTable.symbols[symbolTable.SC] = currSymbol;
//				symbolTable.SC++;
//			}
//
//			/* Classify the data type (.data / .string),
//			 * store it in the memory and update data counters */
//
//		}
//		/* Check for a other instruction directives (.extern / .entry) */
//		else
//		{
//			Boolean DoesContainExternDirective = FALSE;
//			Boolean DoesContainEntryDirective = FALSE;
//
//			if (strcmp(currProgramLine + currSymbolIndex, EXTERN_DIRECTIVE) == EQUAL_STRINGS)
//			{
//				DoesContainExternDirective = TRUE;
//			}
//
//			if (strcmp(currProgramLine + currSymbolIndex, ENTRY_DIRECTIVE) == EQUAL_STRINGS)
//			{
//				DoesContainEntryDirective = TRUE;
//			}
//
//			/* In case contains other instruction directives (.extern / .entry) */
//			if ((DoesContainExternDirective == TRUE) || (DoesContainEntryDirective == TRUE))
//			{
//				/* In case it is an .extern instruction directive */
//				if (DoesContainExternDirective == TRUE)
//				{
//					int externSymbolLength = strlen(currCommandTokens);
//
//					if (externSymbolLength > 0)
//					{
//						/* Insert the given symbol to the extern symbol list */
//						Symbol currExternSymbol;
//						currExternSymbol.address = NULL;
//
//						strncpy(currSymbol.symbolName, currCommandTokens + currSymbolIndex, externSymbolLength - 1);
//						currSymbol.symbolType = EXTERN;
//
//						externSymbolTable.symbols[externSymbolTable.SC] = currSymbol;
//						externSymbolTable.SC++;
//					}
//					else
//					{
//						/* TODO : Notify invalid extern symbol name */
//					}
//				}
//			}
//			/* In case it doesn't contain other instruction directives */
//			else
//			{
//				/* If a symbol was found insert the it to the symbol table as a code symbol */
//				if (DoesContainSymbol == TRUE)
//				{
//					Symbol currSymbol;
//					currSymbol.address = programMemory.IC;
//					strncpy(currSymbol.symbolName, currCommandTokens, currSymbolIndex -1);
//					currSymbol.symbolType = CODE;
//
//					symbolTable.symbols[symbolTable.SC] = currSymbol;
//					symbolTable.SC++;
//				}
//				/* TODO : check that the rest of the line is a valid command,
//				 * if it isn't notify an error */
//
//				/* TODO : evaluate the rest of the command and calculate it's length in words */
//				/*programMemory.IC =*/
//			}
//		}
//	}
//
//	return firstPassSucceeded;
//}

/* ----------------------------------------------------------------------------
 *	Function Definition End
 * ----------------------------------------------------------------------------*/
